
#include <sun/entry_point/user_entry_point.hpp>
#include <needs_more_ninja/template/output_message.hpp>

#include <sun/application/application.hpp>
#include <sun/window/window.hpp>

#include <sun/game_state/game_state.hpp>

#include <sun/lua/lua.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>

bool test_function( int i ) { nmn::output_message( "C-Style Function: %1%", i ); return true; }

struct test_functor
{
	bool operator()( int i ) const
	{
		nmn::output_message( "Functor: %1%", i );
		return true;
	}
};

class member_function_test
{
public:
	bool f( int i )
	{
		nmn::output_message( "C++ Member Function: %1%", i );
		return true;
	}
};

member_function_test g_test;

luabind::object get_c_function( )
{
	return luabind::make_function( sun::lua::g_state, test_function ); 
}

luabind::object get_functor( )
{
	return luabind::make_function(	sun::lua::g_state,
									test_functor(),
									boost::mpl::vector<bool, int>(),
									luabind::detail::null_type() );
}

luabind::object get_member_function( )
{
	return luabind::make_function(	sun::lua::g_state,
									boost::bind( &member_function_test::f, &g_test, _1 ),
									boost::mpl::vector<bool, int>(),
									luabind::detail::null_type() );
}

class my_application : public sun::application
{
	/**
	 *	User call back to initialise the application window and push the initial game state.
	 *	@param command_line The command line arguments sent into the application.
	 *	@return false if the application could not be begun.
	 *	\author Sean Tromans
	 */
	bool begin_application( sun::command_line_type const & /*command_line*/ )
	{
		//	window
		if ( !sun::lua::do_file( "./resources/lua/begin_application.lua" ) )
		{
			return false;
		}

		try
		{
			m_state = luabind::call_function<sun::state_ptr>( luabind::globals(sun::lua::g_state)["begin_application"] );
		}
		catch ( luabind::error const & e )
		{
			nmn::output_message( e.what( ) );
			nmn::output_message( "%1%", luabind::object( luabind::from_stack( e.state( ), -1 ) ) );
			return false;
		}
		catch ( luabind::cast_failed const & e )
		{
			nmn::output_message( e.what( ) );
			nmn::output_message( e.info( ).name( ) );
			return false;
		}

		return true;
	}

	/**
	 *	User callback to initialise the game specific Lua interface.
	 *	@param L The Lua state being initialised.
	 *	@return false if the interface could not be created.
	 *	\author Sean Tromans
	 */
	bool initialise_lua_interface( lua_State * L )
	{
		luabind::module( L )
		[
			luabind::def( "get_c_function", get_c_function ),
			luabind::def( "get_functor", get_functor ),
			luabind::def( "get_member_function", get_member_function )
		];

		return true;
	}
};

int	sun_entry_point( sun::command_line_type const & arguments )
{
	my_application app;
	app.run( arguments );

	return 0;
}
